#pragma once
#include <cstdint>
#include <string>
#include <memory>
#include <list>
#include <boost/lexical_cast.hpp>
#include <xscript/xdefs.h>

class XToken : public std::enable_shared_from_this<XToken> {
  public:
    enum XToken_Enum {
        XToken_EnumBoolean = 0,
        XToken_EnumInteger,
        XToken_EnumFloat,
        XToken_EnumString,
        XToken_EnumVariable,
        XToken_EnumKeyword,
        XToken_EnumPunctuator
    };
    XToken() = delete;
    explicit XToken(uint32_t line, uint32_t block, const std::wstring& module = L""):
        moduleName(module),
        lineNumber(line),
        blockNumber(block) {
    }
    virtual ~XToken() = default;
  public:
    std::wstring getModuleName()const {return moduleName;}
    uint32_t getLineNumber() const {return lineNumber;}
    uint32_t getBlock() const {return blockNumber;}
    virtual std::wstring getString() const = 0;
    virtual std::wstring toString() const;
    virtual XToken_Enum getTokenType() const = 0;
    virtual std::shared_ptr<XTokenBoolean> asBoolean() {return nullptr;}
    virtual std::shared_ptr<XTokenInteger> asInteger() {return nullptr;}
    virtual std::shared_ptr<XTokenFloat> asFloat() {return nullptr;}
    virtual std::shared_ptr<XTokenString> asString() {return nullptr;}
    virtual std::shared_ptr<XTokenKeyword> asKeyword() {return nullptr;}
    virtual std::shared_ptr<XTokenVariable> asVariable() {return nullptr;}
    virtual std::shared_ptr<XTokenPunctuator> asPunctuator() {return nullptr;}
  private:
    std::wstring moduleName;
    uint32_t lineNumber = ~0;
    uint32_t blockNumber = ~0;
};

using XTokenList = std::vector<XTokenPtr>;

struct XTokenLine {
    uint32_t line = 0;
    XTokenList tokens;
};

using XTokenBlock = std::vector<XTokenLine>;

class XTokenBoolean : public XToken {
  public:
    XTokenBoolean() = delete;
    XTokenBoolean(const std::wstring& flag, uint32_t line, uint32_t block);
  public:
    bool getValue()const;
    std::wstring getString() const override;
    XToken_Enum getTokenType() const override;
    std::shared_ptr<XTokenBoolean> asBoolean() override;
  private:
    bool value;
};

class XTokenInteger : public XToken {
  public:
    XTokenInteger() = delete;
    XTokenInteger(const std::wstring& flag, uint32_t line, uint32_t block);
  public:
    int32_t getValue()const;
    std::wstring getString() const override;
    XToken_Enum getTokenType() const override;
    std::shared_ptr<XTokenInteger> asInteger() override;
  private:
    int32_t value = 0;
};

class XTokenFloat : public XToken {
  public:
    XTokenFloat() = delete;
    XTokenFloat(const std::wstring& flag, uint32_t line, uint32_t block):
        XToken(line, block) {
        value = boost::lexical_cast<float>(flag);
    }
  public:
    float getValue()const {return value;}
    std::wstring getString() const override {
        return std::to_wstring(value);
    }
    XToken_Enum getTokenType() const override {
        return XToken_EnumFloat;
    }
    std::shared_ptr<XTokenFloat> asFloat() override {
        return std::dynamic_pointer_cast<XTokenFloat>(shared_from_this());
    }
  private:
    float value = 0;
};

class XTokenString : public XToken {
  public:
    XTokenString() = delete;
    XTokenString(const std::wstring& flag, uint32_t line, uint32_t block):
        XToken(line, block),
        value(flag) {
    }
    virtual ~XTokenString() = default;
  public:
    std::wstring getValue()const {return value;}
    std::wstring getString() const override {
        return value;
    }
    XToken_Enum getTokenType() const override {
        return XToken_EnumString;
    }
    std::shared_ptr<XTokenString> asString() override {
        return std::dynamic_pointer_cast<XTokenString>(shared_from_this());
    }
  private:
    std::wstring value;
};

class XTokenKeyword : public XToken {
  public:
    enum XTokenKeyword_Enum {
        XTokenKeyword_EnumLet = 0,
        XTokenKeyword_EnumSet,
        XTokenKeyword_EnumMax,
    };

    XTokenKeyword() = delete;
    XTokenKeyword(const std::wstring& flag, uint32_t line, uint32_t block);
  public:
    std::wstring getString() const override {
        return value;
    }
    XToken_Enum getTokenType() const override {
        return XToken_EnumKeyword;
    }
    std::shared_ptr<XTokenKeyword> asKeyword() override {
        return std::dynamic_pointer_cast<XTokenKeyword>(shared_from_this());
    }

    XTokenKeyword_Enum getKeywordFlag()const {
        return keywordType;
    }
  private:
    std::wstring value;
    XTokenKeyword_Enum keywordType;
};

class XTokenVariable : public XToken {
  public:
    XTokenVariable() = delete;
    XTokenVariable(const std::wstring& flag, uint32_t line, uint32_t block):
        XToken(line, block),
        value(flag) {
    }
  public:
    std::wstring getString() const override {
        return value;
    }
    XToken_Enum getTokenType() const override {
        return XToken_EnumVariable;
    }
    std::shared_ptr<XTokenVariable> asVariable() override {
        return std::dynamic_pointer_cast<XTokenVariable>(shared_from_this());
    }
  private:
    std::wstring value;
};

class XTokenPunctuator : public XToken {
  public:
    XTokenPunctuator() = delete;
    XTokenPunctuator(const std::wstring& flag, uint32_t line, uint32_t block):
        XToken(line, block),
        value(flag) {
    }
  public:
    void setString(const std::wstring& flag) {value = flag;}
    std::wstring getString()const override {return value;}
    XToken_Enum getTokenType() const override {
        return XToken_EnumPunctuator;
    }
    std::shared_ptr<XTokenPunctuator> asPunctuator() override {
        return std::dynamic_pointer_cast<XTokenPunctuator>(shared_from_this());
    }
  private:
    std::wstring value;
};
